﻿using System;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using NJabber.Stream.Elements;
using NUnit.Framework;

namespace NJabber.Stream.Elements
{
    public class XmppStreamElementTests : XmppStreamElementTestBase
    {
        private readonly string attributePrefix = "a";
        private readonly string attributeNamespace = "www.testuri.org";
        private readonly string attributeName = "amy";
        private readonly string attributeValue = "lee";

        public XmppStreamElementTests() : base("xmppElement", "www.tempuri.org", "test")
        {
        }

        #region Write method tests

        [Test]
        public void Write_CorrectXmlName_ProducesCorrectXmlElement()
        {
            XmppStreamElement actualElement = new XmppStreamElementImpl(Name);
            XElement expectedElement = CreateXElement(Name);

            VerifyWriteOutput(expectedElement, actualElement);
        }
        
        [Test]
        public void Write_CorrectXmlNameAndXmlNamespaceUri_ProducesCorrectXmlElement()
        {
            XmppStreamElement actualElement = new XmppStreamElementImpl(Name, XmlNamespace.Uri);
            XElement expectedElement = CreateXElement(Name, XmlNamespace.Uri);

            VerifyWriteOutput(expectedElement, actualElement);
        }
        
        [Test]
        public void Write_CorrectXmlNameAndXmlNamespaceUriAndName_ProducesCorrectXmlElement()
        {
            XmppStreamElement actualElement = new XmppStreamElementImpl(Name, XmlNamespace.Name, XmlNamespace.Uri);
            XElement expectedElement = CreateXElement(Name, XmlNamespace.Name, XmlNamespace.Uri);

            VerifyWriteOutput(expectedElement, actualElement);
        }
        
        [Test]
        public void Write_NonEmptyString_ProduceCorrectXmlElementWithContent()
        {
            string payload = Guid.NewGuid().ToString();
            XmppStreamElementImpl xmppElement = new XmppStreamElementImpl(Name);
            XElement expectedElement = CreateXElementWithPayload(Name, payload);

            xmppElement.Content = payload;

            VerifyWriteOutput(expectedElement, xmppElement);
        }

        private XElement CreateXElementWithPayload(string name, string content)
        {
            XElement xmppElement = CreateXElement(name);
            xmppElement.SetValue(content);
            return xmppElement;
        }

        [Test]
        public void Write_EmptyAttributeValue_WillNotWriteAttribute()
        {
            XmppStreamElementImpl xmppElement = new XmppStreamElementImpl(Name);
            XElement expectedElement = CreateXElement(Name);
            xmppElement["nullAttribute"] = null;
            xmppElement["emptyAttribute"] = "";

            VerifyWriteOutput(expectedElement, xmppElement);
        }

        #endregion

        #region Attribute method tests

        [Test]
        public void GetAttributeValue_NotEmptyNameValue_ReturnsLastSettedValue()
        {
            XmppStreamElementImpl xmppElement = new XmppStreamElementImpl(Name);
            xmppElement[attributeName, attributePrefix, attributeNamespace] = attributeValue;

            Assert.AreEqual(attributeValue, xmppElement[attributeName, attributePrefix, attributeNamespace]);
        }

        [Test]
        public void SetAttributeValue_NonEmptyValue_WritesAttributeWithGivenValue()
        {
            XmppStreamElementImpl xmppElement = new XmppStreamElementImpl(Name);
            XElement expectedElement = CreateXElement(Name);
            expectedElement.SetAttributeValue(attributeName, attributeValue);
            xmppElement[attributeName] = attributeValue;

            VerifyWriteOutput(expectedElement, xmppElement);
        }

        [Test]
        public void SetAttributeValue_NonEmptyNamespaceAndValue_WritesAttributeWithPrefix()
        {
            XmppStreamElementImpl xmppElement = new XmppStreamElementImpl(Name);
            xmppElement[attributeName, attributePrefix, attributeNamespace] = attributeValue;

            XElement expectedElement = CreateXElement(Name);
            expectedElement.SetAttributeValue(XNamespace.Xmlns + attributePrefix, attributeNamespace);
            expectedElement.SetAttributeValue(XNamespace.Get(attributeNamespace) + attributeName, attributeValue);

            VerifyWriteOutput(expectedElement, xmppElement);
        }

        #endregion
        
        [Test]
        [Ignore]
        public void Write_NameXmlNamespaceNameAndUri_ProducesSameXmlAsToString()
        {
            XmppStreamElement xmppElement = new XmppStreamElementImpl(Name, XmlNamespace.Uri, XmlNamespace.Name);
            XmlNode node1 = CreateXmlNodeUsingToStringMethod(xmppElement);
            XmlNode node2 = CreateXmlNodeUsingWriteMethod(xmppElement);

            //VerifyWriteOutput(node1, node2);
        }

        private XmlNode CreateXmlNodeUsingToStringMethod(XmppStreamElement xmppElement)
        {
            XmlDocument xmlNode = new XmlDocument();
            xmlNode.LoadXml(xmppElement.ToString());
            return xmlNode;
        }

        private XmlNode CreateXmlNodeUsingWriteMethod(XmppStreamElement xmppElement)
        {
            XmlDocument xmlNode = new XmlDocument();
            StringBuilder xmlWriterOutput = new StringBuilder();

            XmlWriter xmlWriter = CreateWriter(xmlWriterOutput);
            xmppElement.Write(xmlWriter);
            xmlWriter.Close();

            xmlNode.LoadXml(xmlWriterOutput.ToString());
            return xmlNode;
        }

        private XmlWriter CreateWriter(StringBuilder output)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.OmitXmlDeclaration = true;
            return XmlWriter.Create(output, settings);
        }

        #region Equals method test

        [Test]
        public void Equals_NullParameter_ReturnsFalse()
        {
            XmppStreamElement element = new XmppStreamElementImpl(Name);
            
            Assert.That(element.Equals(null), Is.False);
        }

        [Test]
        public void Equals_ParameterNotBelongsToXmppStreamElementType_ReturnsFalse()
        {
            XmppStreamElement element = new XmppStreamElementImpl(Name);
            object other = new DateTime();

            Assert.That(element.Equals(other), Is.False);
        }

        [Test]
        public void Equals_ElementsWithSameNamesAndWithoutNamespaces_ReturnsTrue()
        {
            XmppStreamElement element1 = new XmppStreamElementImpl("e1");
            XmppStreamElement element2 = new XmppStreamElementImpl("e1");

            Assert.That(element1.Equals(element2), Is.True);
        }

        [Test]
        public void Equals_ElementsWithDifferentNamesAndWithoutNamespaces_ReturnsFalse()
        {
            XmppStreamElement element1 = new XmppStreamElementImpl("e1");
            XmppStreamElement element2 = new XmppStreamElementImpl("e2");

            Assert.That(element1.Equals(element2), Is.False);
        }

        [Test]
        public void Equals_ElementsWithSameNamesAndDifferentNamespaces_ReturnsFalse()
        {
            XmppStreamElement element1 = new XmppStreamElementImpl("marilyn", "manson");
            XmppStreamElement element2 = new XmppStreamElementImpl("marilyn", "monroe");

            Assert.That(element1.Equals(element2), Is.False);
        }

        [Test]
        public void Equals_ElementsWithSameNamesAndSameNamespaces_ReturnsTrue()
        {
            XmppStreamElement element1 = new XmppStreamElementImpl("amy", "lee");
            XmppStreamElement element2 = new XmppStreamElementImpl("amy", "lee");

            Assert.That(element1.Equals(element2), Is.True);
        }

        [Test]
        public void Equals_ElementsWithDifferentNamesAndSameNamespaces_ReturnsFalse()
        {
            XmppStreamElement element1 = new XmppStreamElementImpl("marilyn", "manson");
            XmppStreamElement element2 = new XmppStreamElementImpl("charles", "manson");

            Assert.That(element1.Equals(element2), Is.False);
        }

        [Test]
        public void Equals_ElementsWithDifferendAttributes_ReturnsFalse()
        {
            XmppStreamElementImpl element1 = new XmppStreamElementImpl("playboy");
            XmppStreamElementImpl element2 = new XmppStreamElementImpl("playboy");

            element1["a1"] = "v1";
            element2["a2"] = "v2";

            Assert.That(element1.Equals(element2), Is.False);
        }
        
        [Test]
        public void Equals_ElementsWithSameAttributes_ReturnsTrue()
        {
            XmppStreamElementImpl element1 = new XmppStreamElementImpl("el");
            XmppStreamElementImpl element2 = new XmppStreamElementImpl("el");

            element1["a0"] = "v0";
            element2["a0"] = "v0";

            Assert.That(element1.Equals(element2), Is.True);
        }

        [Test]
        public void Equals_ElementsWithSameAttributeNamesAndDifferentAttributeValues_ReturnsFalse()
        {
            XmppStreamElementImpl element1 = new XmppStreamElementImpl("el");
            XmppStreamElementImpl element2 = new XmppStreamElementImpl("el");

            element1["a1"] = "v1";
            element2["a1"] = "v2";

            Assert.That(element1.Equals(element2), Is.False);
        }

        #endregion
    }
}
